﻿/*
 * No greedy ubb reader alogrithm
 *
 * More details please the visio diagram
 *
 * Explain:
 *
 *      Q: What is a Label ? And what is a tag?
 *      A: Well... They are the same except that a label is propably a tag, it may be treated as text after match operations (e.g. a start label without matched close label,
 *          in this case, the start label will mark as 'Label' in UBBContent.Type but 'Text' in UBBContent.DisplayType )
 */

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace Snowdreamist.Text.UBB
{
    public class NoGreedyUBBReader : UBBReader
    {
        public NoGreedyUBBReader(IUBBElementFactory elmfactory)
            : base(elmfactory)
        {
        }

        /// <summary>
        /// The parsing strategy
        /// </summary>
        public override UBBParseStrategy Strategy
        {
            get
            {
                return UBBParseStrategy.NoGreedy;
            }
        }

        protected override IEnumerable<UBBContent> MatchUBBLabels(IEnumerable<UBBContent> ubbContents)
        {
            if (ubbContents == null)
                throw new ArgumentNullException("ubbContents");

            HybridDictionary tagDict = new HybridDictionary(10, false);
            List<UBBContent> contentList = new List<UBBContent>();

            foreach (var ubbContent in ubbContents)
            {
                if (ubbContent.Type == UBBContentType.Text)
                {
                    // text, add to content list
                    contentList.Add(ubbContent);
                }
                else if (ubbContent.Type == UBBContentType.StartLabel)
                {
                    // start label
                    contentList.Add(ubbContent);
                    var labelName = ubbContent.GetLabelName();
                    // check tag queue
                    Queue<UBBContent> contentQueue = null;
                    if (!tagDict.Contains(labelName))
                    {
                        contentQueue = new Queue<UBBContent>();
                        tagDict[labelName] = contentQueue;
                    }
                    else
                    {
                        contentQueue = (Queue<UBBContent>)tagDict[labelName];
                    }
                    // enqueue
                    contentQueue.Enqueue(ubbContent);
                }
                else
                {
                    // close tag, find the first start tag
                    var labelName = ubbContent.GetLabelName();
                    if (!tagDict.Contains(labelName))
                    {
                        // cannot find start tag, fail to close tag, treat as text end push into list
                        contentList.Add(ubbContent);
                    }
                    else
                    {
                        var contentQueue = (Queue<UBBContent>)tagDict[labelName];
                        if (contentQueue.Count != 0)
                        {
                            // match! Dequeue the first to match and mark as label
                            var matchLabel = contentQueue.Dequeue();
                            matchLabel.DisplayType = UBBContentType.StartLabel;
                            // add close label
                            ubbContent.DisplayType = UBBContentType.CloseLabel;
                            contentList.Add(ubbContent);
                        }
                        else
                        {
                            // not match
                            contentList.Add(ubbContent);
                        }
                    }
                }
            }
            // clear
            tagDict.Clear();
            // return
            return contentList;
        }
    }
}